<template>
	<div class="antv" style="height: 100%; width: 100%">
		<div>
			<div ref="containerRef"></div>
			<el-button @click="download2svg">下载为svg</el-button>
			<el-button @click="getData2json">获取JSON</el-button>
		</div>
		<div style="display: flex; height: 500px">
			<div id="stencil" style="position: relative; width: 200px"></div>
			<div ref="containerRef2" id="containerRef2" style="flex: 1"></div>
		</div>
	</div>
</template>

<script>
import { Graph, DataUri, Shape, Addon } from "@antv/x6";
import { stencilNode } from "./options";
// rect 节点的 默认配置
Shape.Rect.config({
	width: 80,
	height: 40,
});
// new Shape.Rect({})
// graph.addNode(rect
const data = {
	// 节点
	nodes: [
		{
			id: "node1", // String，可选，节点的唯一标识
			x: 40, // Number，必选，节点位置的 x 值
			y: 40, // Number，必选，节点位置的 y 值
			width: 80, // Number，可选，节点大小的 width 值
			height: 40, // Number，可选，节点大小的 height 值
			label: "hello", // String，节点标签
			shape: "rect", // 默认：rect； 使用 rect 渲染
			attrs: {
				body: {
					fill: "#2ECC71",
					stroke: "#000",
					strokeDasharray: "10,2",
				},
				label: {
					text: "Hello",
					fill: "#333",
					fontSize: 13,
				},
			},
			ports: [{ id: "port1" }],
		},
		{
			id: "node2", // String，节点的唯一标识
			x: 160, // Number，必选，节点位置的 x 值
			y: 180, // Number，必选，节点位置的 y 值
			width: 80, // Number，可选，节点大小的 width 值
			height: 40, // Number，可选，节点大小的 height 值
			label: "world", // String，节点标签
			shape: "ellipse", // 使用 ellipse 渲染
			// 节点样式
			attrs: {
				body: {
					fill: "#F39C12",
					stroke: "#000",
					rx: 16,
					ry: 16,
				},
				label: {
					text: "World",
					fill: "#333",
					fontSize: 18,
					fontWeight: "bold",
					fontVariant: "small-caps",
				},
			},
		},
	],
	// 边
	edges: [
		{
			source: { cell: "node1", port: "port1" }, // 必须，起始点 (节点，节点id，节点链接桩，指定点)
			// source: "node1",
			target: "node2", // String，必须，目标节点 id
			shape: "edge", // 边线条的类型， 默认：edge
			// 节点样式
			attrs: {
				line: {
					stroke: "orange",
					// targetMarker: {}, // 对象为空时，可以屏蔽shape 为 edge的箭头
				},
			},
			// labels: [
			// 	{
			// 		attrs: { label: { text: "edge1" } },
			// 	},
			// ],
		},
	],
};
export default {
	data() {
		return {
			graph: null,
		};
	},
	mounted() {
		this.init1();
		this.init2();
	},
	beforeDestroy() {
		this.graph.dispose();
	},
	methods: {
		init1() {
			const graph = new Graph({
				container: this.$refs.containerRef,
				width: 400,
				height: 300,
				panning: true, // 开启抓手拖拽整个画布 ，默认 false
				// 画布背景
				background: {
					color: "#000000",
				},
				// 画布网格
				grid: {
					size: 10,
					visible: true,
				},
			});
			this.graph = graph;

			graph.on("node:click", e => {
				console.log("node", e);
			});
			graph.on("edge:click", e => {
				console.log("edge", e);
			});

			// 渲染画布
			graph.fromJSON(data);

			// 缩放画布
			// graph.zoom(-0.5);

			// 内容居中
			// graph.centerContent();

			// 平移画布内容，平移后下载会有问题
			// graph.translate(80, 40);
		},
		init2() {
			// 初始化 画布
			const graph = new Graph({
				// 容器
				container: this.$refs.containerRef2,
				// 网格
				grid: true,
				// 宽高，默认100%
				// width: 400,
				// height: 300,
				// 开启抓手拖拽整个画布 ，默认 false
				panning: true,
				// 画布背景
				background: {
					color: "#000000",
				},
				// 控制滚轮行为
				mousewheel: {
					enabled: true,
					zoomAtMousePosition: true,
					modifiers: "ctrl",
					minScale: 0.5,
					maxScale: 3,
				},
				// 配置全局的连线规则
				connecting: {
					router: {
						name: "manhattan",
						args: {
							padding: 1,
						},
					},
					connector: {
						name: "rounded",
						args: {
							radius: 8,
						},
					},
					anchor: "center",
					connectionPoint: "anchor",
					allowBlank: false,
					snap: {
						radius: 20,
					},
					createEdge() {
						return new Shape.Edge({
							attrs: {
								line: {
									stroke: "#A2B1C3",
									strokeWidth: 2,
									targetMarker: {
										name: "block",
										width: 12,
										height: 8,
									},
								},
							},
							zIndex: 0,
						});
					},
					validateConnection({ targetMagnet }) {
						return !!targetMagnet;
					},
				},
				// 指定触发某种交互时的高亮样式
				highlighting: {
					// 当链接桩可以被链接时，在链接桩外围渲染一个 2px 宽的红色矩形框
					magnetAdsorbed: {
						name: "stroke",
						args: {
							padding: 4,
							attrs: {
								fill: "#5F95FF",
								strokeWidth: 2,
								stroke: "red",
							},
						},
					},
				},
				// 缩放节点
				resizing: true,
				// 旋转节点
				rotating: true,
				// 点选/框选  ，按住ctrl可多选 ，框选与手抓 panning 冲突
				selecting: {
					enabled: true,
					rubberband: true,
					showNodeSelectionBox: true,
				},
				// 开启对齐线
				snapline: true,
				// 开启键盘快捷键监听
				keyboard: true,
				// 剪贴板 开启后可以调用api复制节点
				clipboard: true,
				// 开启画布撤销/重做能力
				history: true,
			});

			// 初始化 stencil 一般放在侧边栏
			const stencil = new Addon.Stencil({
				title: "流程图",
				// 指定画布
				target: graph,
				search: true,
				// 每个分组宽度
				stencilGraphWidth: 200,
				// 每个分组的高度 默认值为800，但是不生效
				// stencilGraphHeight: 800,
				// 分组是否可折叠
				collapsable: true,
				// 分组
				groups: [
					{
						title: "基础流程图",
						name: "group1",
						//  分组高度
						graphHeight: 200,
					},
					{
						title: "系统设计图",
						name: "group2",
						graphHeight: 200,
					},
				],
				// 分组的布局
				layoutOptions: {
					// 网格布局的列数
					columns: 2,
					// 列宽
					columnWidth: 80,
					// 行高
					rowHeight: 55,
				},
			});

			// 渲染侧边栏
			document.getElementById("stencil").appendChild(stencil.container);

			// 画布绑定事件
			this.bindGrapHEvent(graph);

			// stencil 自定义节点
			this.customStencilNode(graph, stencil);
		},
		// 绑定快捷操作和事件
		bindGrapHEvent(graph) {
			// 控制连接桩显示/隐藏
			const showPorts = (ports, show) => {
				for (let i = 0, len = ports.length; i < len; i = i + 1) {
					ports[i].style.visibility = show ? "visible" : "hidden";
				}
			};
			// 鼠标移入 显示连接桩
			graph.on("node:mouseenter", () => {
				const container = document.getElementById("containerRef2");
				const ports = container.querySelectorAll(".x6-port-body");
				showPorts(ports, true);
			});
			// 鼠标移出 隐藏连接桩
			graph.on("node:mouseleave", () => {
				const container = document.getElementById("containerRef2");
				const ports = container.querySelectorAll(".x6-port-body");
				showPorts(ports, false);
			});

			// 双击事件
			graph.on("cell:dblclick", ({ cell }) => {
				// TODO : 动态修改属性，【body为配置文件中attrs下的属性，fill为body下的属性】
				// {
				// 	attrs: {
				// 		body: {
				// 			strokeWidth: 1,
				// 			stroke: "",
				// 			fill: "",
				// 		},
				// 		text: {
				// 			fontSize: 12,
				// 			fill: "",
				// 		},
				// 	},
				// }
				const res = prompt("请输入字体大小");
				cell.attr("body/fill", "red");
				cell.attr("text/fontSize", res);
			});

			// 复制 节点
			graph.bindKey(["meta+c", "ctrl+c"], () => {
				const cells = graph.getSelectedCells();
				if (cells.length) {
					graph.copy(cells);
				}
				return false;
			});
			// 剪切 节点
			graph.bindKey(["meta+x", "ctrl+x"], () => {
				const cells = graph.getSelectedCells();
				if (cells.length) {
					graph.cut(cells);
				}
				return false;
			});
			// 粘贴 节点
			graph.bindKey(["meta+v", "ctrl+v"], () => {
				if (!graph.isClipboardEmpty()) {
					const cells = graph.paste({ offset: 32 });
					graph.cleanSelection();
					graph.select(cells);
				}
				return false;
			});

			// 撤销 操作
			graph.bindKey(["meta+z", "ctrl+z"], () => {
				if (graph.history.canUndo()) {
					graph.history.undo();
				}
				return false;
			});
			// 重做 操作 就相当于平时的 ctrl + y
			graph.bindKey(["meta+y", "ctrl+y"], () => {
				console.log(graph.history.canRedo());
				if (graph.history.canUndo()) {
					graph.history.redo();
				}
				return false;
			});

			// 全选 会把页面也选中
			graph.bindKey(["meta+a", "ctrl+a"], () => {
				const nodes = graph.getNodes();
				if (nodes) {
					graph.select(nodes);
				}
			});

			// 删除 节点
			graph.bindKey("backspace", () => {
				const cells = graph.getSelectedCells();
				if (cells.length) {
					graph.removeCells(cells);
				}
			});
		},
		// 自定义Stencil节点
		customStencilNode(graph, stencil) {
			// 以下是包含了注册节点，创建节点，其实可以直接创建节点，修改一下配置文件就可以

			// 注册节点类
			stencilNode.forEach(node => Graph.registerNode(...node));

			// 创建自定义节点
			const r1 = graph.createNode({
				shape: "custom-rect",
				label: "开始", // 可以配置在外面
				attrs: {
					// label: {
					// 	text: "开始",
					// },
					body: {
						rx: 20,
						ry: 26,
					},
				},
			});
			const r2 = graph.createNode({
				shape: "custom-rect",
				label: "过程",
			});
			const r3 = graph.createNode({
				shape: "custom-rect",
				attrs: {
					label: {
						text: "可选过程",
					},
					body: {
						rx: 6,
						ry: 6,
					},
				},
			});
			const r4 = graph.createNode({
				shape: "custom-polygon",
				attrs: {
					body: {
						refPoints: "0,10 10,0 20,10 10,20",
					},
				},
				label: "决策",
			});
			const r5 = graph.createNode({
				shape: "custom-polygon",
				attrs: {
					body: {
						refPoints: "10,0 40,0 30,20 0,20",
					},
				},
				label: "数据",
			});
			const r6 = graph.createNode({
				shape: "custom-circle",
				label: "连接",
			});
			const r7 = graph.createNode({
				shape: "custom-path",
				width: 10,
				height: 50,
				attrs: {
					body: {
						fill: "#ff0000",
						stroke: "none",
						refD: `M25,43.5 L25,50 L24.5,50 L24.5,43.5 L25,43.5 Z M24.75,41 C25.4403559,41 26,41.5596441 26,42.25 C26,42.9403559 25.4403559,43.5 24.75,43.5 C24.0596441,43.5 23.5,42.9403559 23.5,42.25 C23.5,41.5596441 24.0596441,41 24.75,41 Z M24.75,41.4166667 C24.2897627,41.4166667 23.9166667,41.7897627 23.9166667,42.25 C23.9166667,42.7102373 24.2897627,43.0833333 24.75,43.0833333 C25.2102373,43.0833333 25.5833333,42.7102373 25.5833333,42.25 C25.5833333,41.7897627 25.2102373,41.4166667 24.75,41.4166667 Z M25,35 L25,36 L26,36 L26,36.5 L25,36.5 L25,41 L24.5,41 L24.5,36.5 L23.5,36.5 L23.5,36 L24.5,36 L24.5,35 L25,35 Z M25,20 L25,22 L26.5,22 L26.5,32 L25,32 L25,34 L24.5,34 L24.5,32 L23,32 L23,22 L24.5,22 L24.5,20 L25,20 Z M24.75,10 C25.4403559,10 26,10.5596441 26,11.25 C26,11.8544094 25.5710287,12.3586272 25.0009341,12.4748055 L25,19 L24.5,19 L24.5000506,12.4750057 C23.9294653,12.3592077 23.5,11.8547574 23.5,11.25 C23.5,10.5596441 24.0596441,10 24.75,10 Z M24.75,10.5 C24.3357864,10.5 24,10.8357864 24,11.25 C24,11.6642136 24.3357864,12 24.75,12 C25.1642136,12 25.5,11.6642136 25.5,11.25 C25.5,10.8357864 25.1642136,10.5 24.75,10.5 Z M25,5 L25,6 L26,6 L26,6.5 L25,6.5 L25,10 L24.5,10 L24.5,6.5 L23.5,6.5 L23.5,6 L24.5,6 L24.5,5 L25,5 Z M25,0 L25,4 L24.5,4 L24.5,0 L25,0 Z`,
					},
					label: {
						text: "场站开关",
						fill: "#888",
						fontSize: 12,
						fontWeight: 600,
						refY: "110%",
						textVerticalAnchor: "top",
					},
				},
			});

			stencil.load([r1, r2, r3, r4, r5, r6], "group1");
			stencil.load([r7], "group2");
		},
		download2svg() {
			this.graph.toSVG(dataUri => {
				DataUri.downloadDataUri(DataUri.svgToDataUrl(dataUri), "chart.svg");
			});
		},
		getData2json() {
			console.log(this.graph.toJSON());
		},
	},
};
</script>

<style lang="scss" scoped></style>
